/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com 

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef TRI_3D_OBJECT_H_
#define TRI_3D_OBJECT_H_


#include "texture.h"
#include "entity.h"
#include "base/utils.h"
#include "base/system.h"
#include "base/logging.h"
#include "material.h"

namespace tri{

template<class T>
class TriBuffer{
public:
	TriBuffer(int vectorSize = 3):mBufferSize(0),mBuffer(NULL){
		mVectorSize = vectorSize;
	}
	
	virtual ~TriBuffer(){
		flush();
	}
	
	int size(){return mBufferSize;}
	T* getBuffer(){return mBuffer;}
	
	void flush(){
		if (mBuffer != NULL){
			delete mBuffer;
			mBuffer = NULL;
			mBufferSize = 0;
		}
	}
	
	bool allocate(int size){
		if(size<=0){
			return false;
		}
		flush();
		mBuffer = new T[size*mVectorSize];
		mBufferSize = size;
		return true;
	}
	
	void assign(int index, T a,T b,T c){
		if (mVectorSize == 2){
			mBuffer[index*2] = a;
			mBuffer[index*2+1] = b;
			return;
		}
		if(index<=mBufferSize){
			mBuffer[index*3] = a;
			mBuffer[index*3+1] = b;
			mBuffer[index*3+2] = c;
		}
	}
	
	void assign(int index, T a,T b){
		if (mVectorSize != 2){
			return;
		}
		if(index<=mBufferSize){
			mBuffer[index*2] = a;
			mBuffer[index*2+1] = b;
		}
	}
	
	void assign(int index, T* v){
		if(index<=mBufferSize){
			for (int i = 0;i<mVectorSize;i++){
				mBuffer[index*mVectorSize + i] = v[i];
			}
		}
	}
	
	T* get(int index){
		if(index<mBufferSize&& index >=0){
			return &mBuffer[index*mVectorSize];	
		}
		return NULL;
	}
	
private:
	
	//attributes
	int mBufferSize; 
	T* mBuffer;
	int mVectorSize;
};

class object_3d : public tri::entity { 
public:
	object_3d();
    virtual ~object_3d();

    virtual void display();
    void calculateNormals();
    
    static object_3d* load3dsMesh(int& m_count,const char* p_filename);
    void setTexture(std::string s);
    
    static void render3DObject(TriBuffer<float>& VertexBuffer,
    							TriBuffer<float>& NormalBuffer,
    							TriBuffer<GLushort>& FaceBuffer,
    							    TriBuffer<float>& TexCBuffer,
    							    tri::Material& Material,
    							    std::string Texture);
    
    TriBuffer<float> mVertexBuffer;
    TriBuffer<float> mNormalBuffer;
    TriBuffer<GLushort> mFaceBuffer;
    TriBuffer<float> mTexCBuffer;
   
    tri::Material mMaterial;
private:
    tri::vector getSurfaceNormal(int faceIndex);
    void updateNormal(int index,tri::vector& n);
    std::string mTexture;
};


class Model3D : public tri::entity{
private:
	std::string mModelGroup;
	
	class ObjectGroup{
	public:
		ObjectGroup();
		virtual ~ObjectGroup();
		int mObjectCount;
		object_3d* mObjects;
	};
	
	static std::map<std::string,ObjectGroup*> mModelList;
	
public:
	Model3D();
	virtual ~Model3D();
	virtual void display();
	
	void setModel(std::string model);
	object_3d* getObject3D(std::string name);
	void getObjectList(std::vector<std::string>& list);
	
	static bool loadModel(std::string key,std::string _file = "");
	static void deleteModel(std::string key);
	static bool exists(std::string key);

};

}

#endif
